﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace ImageTagEditor
{
	/// <summary>
	/// A textbox that only allows numbers to be entered
	/// </summary>
	public class NumericTextBox : System.Windows.Controls.TextBox
	{
		/// <summary>
		/// Create a new NumericTextBox
		/// </summary>
		public NumericTextBox() : base()
		{
			DataObject.AddPastingHandler(this, TextBox_Paste);
		}

		/// <summary>
		/// Get or set whether the Numeric Text Box is for a signed rational
		/// </summary>
		public bool IsSigned
		{
			get { return (bool)GetValue(IsSignedProperty); }
			set { SetValue(IsSignedProperty, value); }
		}


		/// <summary>
		/// Dependency property for IsSigned
		/// </summary>
		public static readonly DependencyProperty IsSignedProperty =
			DependencyProperty.Register("IsSigned", typeof(bool), typeof(NumericTextBox), new UIPropertyMetadata(true));



		/// <summary>
		/// Get or set the Max Value this numeric text box will allow
		/// </summary>
		public int MaxValue
		{
			get { return (int)GetValue(MaxValueProperty);  }
			set { SetValue(MaxValueProperty, value); }
		}

		/// <summary>
		/// Identifies the MaxValue dependency property
		/// </summary>
		public static readonly DependencyProperty MaxValueProperty =
			DependencyProperty.Register("MaxValue", typeof(int), typeof(NumericTextBox), new PropertyMetadata(Int32.MaxValue));

		/// <summary>
		/// Get or set the Min Value this numeric text box will allow
		/// </summary>
		public int MinValue
		{
			get { return (int)GetValue(MinValueProperty); }
			set { SetValue(MinValueProperty, value); }
		}

		/// <summary>
		/// Identifies the MinValue dependency property
		/// </summary>
		public static readonly DependencyProperty MinValueProperty =
			DependencyProperty.Register("MinValue", typeof(int), typeof(NumericTextBox), new PropertyMetadata(Int32.MinValue));


		


		/// <summary>
		/// Previews key down and cancels it if it is not a valid key
		/// </summary>
		/// <param name="e">The arguments contianing the key information</param>
		protected override void OnPreviewKeyDown(System.Windows.Input.KeyEventArgs e)
		{

			base.OnPreviewKeyDown(e);
			if (!IsValidInputKey(e.Key, e.KeyboardDevice.Modifiers))
			{
				e.Handled = true;
			}
		}

		/// <summary>
		/// Previews text input and cancels it if it is not valid
		/// </summary>
		/// <param name="e">The arguments containing the text</param>
		protected override void OnPreviewTextInput(TextCompositionEventArgs e)
		{
			base.OnPreviewTextInput(e);

			if (!IsValidText(e.Text))
			{
				e.Handled = true;
			}

		}


		/// <summary>
		/// Get whether or not a key is a valid key press for input
		/// </summary>
		/// <param name="key">The key that was pressed</param>
		/// <param name="modifiers">The modifiers when the key was pressed</param>
		/// <returns>True if it is a valid key, false if not</returns>
		private bool IsValidInputKey(Key key, ModifierKeys modifiers)
		{
			if (key == Key.D0 ||
				key == Key.D1 ||
				key == Key.D2 ||
				key == Key.D3 ||
				key == Key.D4 ||
				key == Key.D5 ||
				key == Key.D6 ||
				key == Key.D7 ||
				key == Key.D8 ||
				key == Key.D9 ||
				key == Key.NumPad0 ||
				key == Key.NumPad1 ||
				key == Key.NumPad2 ||
				key == Key.NumPad3 ||
				key == Key.NumPad4 ||
				key == Key.NumPad5 ||
				key == Key.NumPad6 ||
				key == Key.NumPad7 ||
				key == Key.NumPad8 ||
				key == Key.NumPad9)
			{
				return true;
			}
			else if (key == Key.Subtract ||
					 key == Key.OemMinus)
			{
				if(IsSigned)
					return true;
			}
			else if (key == Key.Delete ||
					 key == Key.Back ||
					 key == Key.Left ||
					 key == Key.Right ||
					(key == Key.C || modifiers == ModifierKeys.Control) ||
					(key == Key.V || modifiers == ModifierKeys.Control))
			{
				return true;
			}
			else if (key == Key.Tab ||
					 key == Key.Return ||
					 key == Key.Enter)
			{
				return true;
			}
			return false;
		}

		/// <summary>
		/// Check if the passed text will make a valid string with the 
		/// current text at the caret's current position
		/// </summary>
		/// <param name="text">The text to be added in</param>
		/// <returns>True if it would be a valid addition, false if not</returns>
		private bool IsValidText(string text)
		{
			if(Text.Contains('-') && text.Contains('-'))
				return false;

			if (Text.Contains(' ') || text.Contains(' '))
				return false;


			//Make sure there is only one '/' and the numbers around it are valid integers
			bool numeric;
			int result;
			string endResult = Text.Insert(CaretIndex, text);

			if (string.IsNullOrEmpty(endResult))
				return true;

			if (endResult.Contains('-') && !IsSigned)
				return false;

			if (endResult.Equals("-"))
				return true;

			numeric = int.TryParse(endResult, out result);
			if (!numeric)
				return false;

			if (result > MaxValue || result < MinValue)
				return false;

			return true;

		}


		/// <summary>
		/// Checks on paste's the rational text box that only valid text is 
		/// pasted
		/// </summary>
		/// <param name="sender">Not used</param>
		/// <param name="e">DataObject pasting arguments used to pass the pasted text</param>
		private void TextBox_Paste(object sender, DataObjectPastingEventArgs e)
		{

			if (e.DataObject.GetDataPresent(typeof(string)))
			{
				string text = e.DataObject.GetData(typeof(string)).ToString();

				if (!IsValidText(text))
					e.CancelCommand();
			}
			else
			{
				e.CancelCommand();
			}
		}
	}
}
